package com.gitee.apanlh.util.reflection;

import com.gitee.apanlh.util.base.ArrayUtils;
import com.gitee.apanlh.util.base.CollUtils;
import com.gitee.apanlh.util.base.IteratorUtils;
import com.gitee.apanlh.util.base.MapUtils;
import com.gitee.apanlh.util.valid.ValidParam;

import java.lang.reflect.Field;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import java.util.Map;

/**	
 * 	反射字段缓存对象
 * 	<br>可作为静态变量线程安全
 *  
 * 	@author Pan
 */
public class FieldCacheObject {
	
	/** 字段 */
	private Field[] fields;
	/** 字段 */
	private Map<String, Field> fieldMap;
	/** 忽略字段 */
	private List<String> ignoreFields;
	/** 是否忽略静态字段(true忽略静态字段) */
	private boolean ignoreStaticFields;
	/** 缓存Key */
	private String cacheKey;
	
	/**
	 * 	默认构造函数
	 * 	
	 * 	@author Pan
	 */
	FieldCacheObject() { 
		super();
	}
	
	/**	
	 * 	构造函数-自定义忽略静态字段开启
	 * 
	 * 	@author Pan
	 * 	@param 	clazz				类
	 * 	@param 	ignoreStaticFields	true开启忽略静态字段
	 */
	FieldCacheObject(Class<?> clazz, boolean ignoreStaticFields) { 
		this(clazz, ignoreStaticFields, null, null);
	}

	/**	
	 * 	构造函数-默认忽略静态字段开启
	 * 
	 * 	@author Pan
	 * 	@param 	clazz				类
	 * 	@param 	ignoreFieldName	       忽略静态字段名称
	 */
	FieldCacheObject(Class<?> clazz, String ignoreFieldName) {
		this(clazz, true, ignoreFieldName);
	}
	
	/**	
	 * 	构造函数-默认忽略静态字段开启
	 * 
	 * 	@author Pan
	 * 	@param 	clazz				类
	 * 	@param 	ignoreFieldNames	忽略多个字段名称
	 */
	FieldCacheObject(Class<?> clazz, String[] ignoreFieldNames) {
		this(clazz, true, ignoreFieldNames);
	}
	
	/**	
	 * 	构造函数-默认忽略静态字段开启
	 * 
	 * 	@author Pan
	 * 	@param 	clazz				类
	 * 	@param 	ignoreFieldNames	忽略字段名称集合
	 */
	FieldCacheObject(Class<?> clazz, List<String> ignoreFieldNames) {
		this(clazz, true, ignoreFieldNames);
	}

	/**	
	 * 	构造函数-自定义忽略静态字段开启
	 * 	<br>填充忽略字段
	 * 	<br>后续将不可变更集合否则抛出{@code UnsupportedOperationException}
	 * 
	 * 	@author Pan
	 * 	@param 	clazz				  类
	 * 	@param 	ignoreStaticFields	  true开启忽略静态字段
	 * 	@param 	ignoreFieldNames	  忽略字段名称
	 */
	FieldCacheObject(Class<?> clazz, boolean ignoreStaticFields, String ignoreFieldNames) {
		this(clazz, ignoreStaticFields, ignoreFieldNames, null);
	}
	
	/**	
	 * 	构造函数-自定义忽略静态字段开启
	 * 	<br>填充忽略字段
	 * 	<br>后续将不可变更集合否则抛出{@code UnsupportedOperationException}
	 * 
	 * 	@author Pan
	 * 	@param 	clazz				  类
	 * 	@param 	ignoreStaticFields	 true开启忽略静态字段
	 * 	@param 	ignoreFieldNames	  忽略字段名称数组
	 */
	FieldCacheObject(Class<?> clazz, boolean ignoreStaticFields, String[] ignoreFieldNames) {
		this(clazz, ignoreStaticFields, ignoreFieldNames, null);
	}
	
	/**	
	 * 	构造函数-自定义忽略静态字段开启
	 * 	<br>填充忽略字段
	 * 	<br>后续将不可变更集合否则抛出{@code UnsupportedOperationException}
	 * 
	 * 	@author Pan
	 * 	@param 	clazz	 			  类
	 * 	@param 	ignoreStaticFields	 true开启忽略静态字段
	 * 	@param 	ignoreFieldNames	  忽略字段名称集合
	 */
	FieldCacheObject(Class<?> clazz, boolean ignoreStaticFields, List<String> ignoreFieldNames) {
		this(clazz, ignoreStaticFields, ignoreFieldNames, null);
	}
	
	/**
	 * 	构造函数-自定义忽略静态字段开启
	 * 	<br>填充字段
	 * 	<br>填充忽略字段
	 * 	<br>填充字段映射
	 * 	<br>填充缓存Key
	 * 	<br>后续将不可变更集合否则抛出{@code UnsupportedOperationException}
	 * 	
	 * 	@author Pan
	 * 	@param 	clazz				类
	 * 	@param 	ignoreStaticFields	true开启忽略静态字段
	 * 	@param 	ignoreType			忽略字段动态类型(String/String[]/List)
	 * 	@param 	fields				字段数组
	 */
	<T> FieldCacheObject(Class<?> clazz, boolean ignoreStaticFields, T ignoreType, Field[] fields) {
		withCacheKey(FieldCache.createKey(clazz, ignoreStaticFields, ignoreType));
//		Log.get().info("class:[{}] create field cache key:[{}]", StackUtils.getCallClassName(), getCacheKey());
		
		//	忽略字段
		withIgnoreStaticFields(ignoreStaticFields);
		if (ignoreType != null) {
			ignoreFields = CollUtils.newArrayList();
			withIgnoreFields(ignoreType);
			this.ignoreFields = Collections.unmodifiableList(getIgnoreFields());
		}
		//	填充Fields字段
		withFields(clazz);
		//	过滤
		filterFields();
		//	填充
		withFieldMap(this.fields);
		this.fieldMap = Collections.unmodifiableMap(getFieldMap());
	}
	
	/**	
	 * 	获取是否开启忽略静态字段
	 * 	
	 * 	@author Pan
	 * 	@return	boolean	true开启忽略静态字段
	 */
	public boolean hasIgnoreStaticFields() {
		return ignoreStaticFields;
	}
	
	/**	
	 * 	获取字段数组
	 * 	
	 * 	@author Pan
	 * 	@return	Field[]
	 */
	public Field[] getFields() {
		return fields;
	}
	
	/**	
	 * 	获取字段映射Map
	 * 	<br>后续将不可变更集合否则抛出UnsupportedOperationException异常
	 * 
	 * 	@author Pan
	 * 	@return	Map
	 */
	public Map<String, Field> getFieldMap() {
		return fieldMap;
	}
	
	/**	
	 * 	获取忽略字段集合
	 * 	<br>后续将不可变更集合否则抛出UnsupportedOperationException异常
	 * 
	 * 	@author Pan
	 * 	@return	List
	 */
	public List<String> getIgnoreFields() {
		return ignoreFields;
	}
	
	/**	
	 * 	获取缓存Key
	 * 	
	 * 	@author Pan
	 * 	@return	String
	 */
	String getCacheKey() {
		return cacheKey;
	}
	
	/**
	 * 	获取Fields时过滤字段
	 * 	<br>自定义反射实体配置
	 * 	
	 * 	@author Pan
	 */
	void filterFields() {
		if (ValidParam.isEmpty(this.fields)) {
			return ;
		}
		boolean hasIgnoreFieldsEmpty = ValidParam.isEmpty(this.getIgnoreFields());
		boolean hasIgnoreStaticFields = this.hasIgnoreStaticFields();
		
		//	全反射字段直接返回
		if (hasIgnoreFieldsEmpty && !hasIgnoreStaticFields) {
			return ;
		}

		Field[] newFields = new Field[this.fields.length];
		int nextIndex = 0;
		
		for (int i = 0; i < this.fields.length; i++) {
			Field field = this.fields[i];
			//	开启忽略静态及字段为静态忽略则跳过循环，忽略字段集合不为空则跳过循环;
			if ((hasIgnoreStaticFields && ReflectionUtils.isStaticField(field)) || CollUtils.findOne(ignoreFields, name -> name.equals(field.getName())) != null) {
				continue;
			}
			newFields[nextIndex++] = field;
		}
		
		if (newFields[newFields.length - 1] == null) {
			newFields = ArrayUtils.copy(newFields, nextIndex);
		}
		//	重新赋值
		this.fields = newFields;
	}

	/**	
	 * 	填充-是否开启忽略静态字段
	 * 	
	 * 	@author Pan
	 * 	@param 	ignoreStaticFields	true忽略静态字段
	 * 	@return	ReflectionEntityConfig
	 */
	FieldCacheObject withIgnoreStaticFields(boolean ignoreStaticFields) {
		this.ignoreStaticFields = ignoreStaticFields;
		return this;
	}
	
	/**	
	 * 	添加-忽略字段
	 * 	
	 * 	@author Pan
	 * 	@param 	obj	对象
	 * 	@return	FieldCacheObject
	 */
	@SuppressWarnings("unchecked")
	<T> FieldCacheObject withIgnoreFields(T obj) {
		if (obj instanceof String) {
			this.ignoreFields.add((String) obj);
			return this;
		}
		
		if (obj instanceof String[]) {
			this.ignoreFields.addAll(Arrays.asList((String[]) obj));
			return this;
		}
		
		if (obj instanceof List) {
			this.ignoreFields.addAll((List<String>) obj);
			return this;
		}
		return this;
	}
	
	/**	
	 * 	填充-Field字段数组
	 * 	
	 * 	@author Pan
	 * 	@param 	clazz		类
	 * 	@return	FieldCacheObject
	 */
	FieldCacheObject withFields(Class<?> clazz) {
		this.fields = ReflectionUtils.FIELD_PROVIDER.getDeclaredFields(clazz);
		return this;
	}
	
	/**	
	 * 	填充-字段映射
	 * 	<br>将自动字段开启权限
	 * 	
	 * 	@author Pan
	 * 	@param 	filterFields	过滤字段
	 * 	@return	FieldCacheObject
	 */
	FieldCacheObject withFieldMap(Field[] filterFields) {
		this.fieldMap = MapUtils.newHashMap(newMap -> IteratorUtils.array(filterFields, field -> {
			ReflectionUtils.setAccessible(field);
			newMap.put(field.getName(), field);
		}));
		return this;
	}

	/**	
	 * 	填充-cacheKey
	 * 	
	 * 	@author Pan
	 * 	@param 	key		已经生成好的键
	 * 	@return	String
	 */
	String withCacheKey(String key) {
		this.cacheKey = key;
		return this.cacheKey;
	}

	@Override
	public boolean equals(Object o) {
		if (this == o) return true;
		if (o == null || getClass() != o.getClass()) return false;

		FieldCacheObject that = (FieldCacheObject) o;

		if (!ignoreFields.equals(that.ignoreFields)) return false;
		return cacheKey.equals(that.cacheKey);
	}

	@Override
	public int hashCode() {
		int result = ignoreFields.hashCode();
		result = 31 * result + cacheKey.hashCode();
		return result;
	}

	/**
	 * 	创建-反射字段缓存类
	 * 	<br>默认开启忽略静态字段
	 * 	
	 * 	@author Pan
	 * 	@param	clazz	类
	 * 	@return	FieldCacheObject
	 */
	public static FieldCacheObject create(Class<?> clazz) {
		return create(clazz, true);
	}
	
	/**	
	 * 	创建-反射字段缓存类
	 * 	<br>默认开启忽略静态字段
	 * 	<br>自定义填充忽略字段
	 * 
	 * 	@author Pan
	 * 	@param	clazz		    类
	 * 	@param 	ignoreFieldName	忽略字段名称
	 * 	@return	FieldCacheObject
	 */
	public static FieldCacheObject create(Class<?> clazz, String ignoreFieldName) {
		return create(clazz, true, ignoreFieldName);
	}
	
	/**	
	 * 	创建-反射字段缓存类
	 * 	<br>默认开启忽略静态字段
	 * 	<br>自定义填充忽略字段
	 * 
	 * 	@author Pan
	 * 	@param 	clazz				类
	 * 	@param 	ignoreFieldNames	忽略一个或多个字段名称
	 * 	@return	FieldCacheObject
	 */
	public static FieldCacheObject create(Class<?> clazz, String... ignoreFieldNames) {
		return create(clazz, true, ignoreFieldNames);
	}
	
	/**	
	 * 	创建-反射字段缓存类
	 * 	<br>默认开启忽略静态字段
	 * 	<br>自定义填充忽略字段
	 * 
	 * 	@author Pan
	 * 	@param	clazz				类
	 * 	@param 	ignoreFieldNames	忽略字段名称集合
	 * 	@return	FieldCacheObject
	 */
	public static FieldCacheObject create(Class<?> clazz, List<String> ignoreFieldNames) {
		return create(clazz, true, ignoreFieldNames);
	}
	
	/**	
	 * 	创建-反射字段缓存类
	 * 	<br>自定义忽略静态字段开启
	 * 	
	 * 	@author Pan
	 * 	@param	clazz				类
	 * 	@param	ignoreStaticFields	true开启忽略静态字段
	 * 	@return	FieldCacheObject
	 */
	public static FieldCacheObject create(Class<?> clazz, boolean ignoreStaticFields) {
		return new FieldCacheObject(clazz, ignoreStaticFields);
	}
	
	/**	
	 * 	创建-反射字段缓存类
	 * 	<br>自定义忽略静态字段开启
	 * 	<br>自定义填充忽略字段
	 * 	
	 * 	@author Pan
	 * 	@param	clazz				  类
	 * 	@param 	ignoreStaticFields	 true开启忽略静态字段
	 * 	@param 	ignoreFieldName		  忽略字段名称
	 * 	@return	FieldCacheObject
	 */
	public static FieldCacheObject create(Class<?> clazz, boolean ignoreStaticFields, String ignoreFieldName) {
		return new FieldCacheObject(clazz, ignoreStaticFields, ignoreFieldName);
	}
	
	/**	
	 * 	创建-反射字段缓存类
	 * 	<br>自定义忽略静态字段开启
	 * 	<br>自定义填充忽略字段
	 * 	
	 * 	@author Pan
	 * 	@param	clazz				  类
	 * 	@param 	ignoreStaticFields	 true开启忽略静态字段
	 * 	@param 	ignoreFieldNames	 忽略字段名称集合
	 * 	@return	FieldCacheObject
	 */
	public static FieldCacheObject create(Class<?> clazz, boolean ignoreStaticFields, String... ignoreFieldNames) {
		return new FieldCacheObject(clazz, ignoreStaticFields, ignoreFieldNames);
	}
	
	/**	
	 * 	创建-反射字段缓存类
	 * 	<br>自定义忽略静态字段开启
	 * 	<br>自定义填充忽略字段
	 * 
	 * 	@author Pan
	 * 	@param	clazz				  类
	 * 	@param 	ignoreStaticFields	 true开启忽略静态字段
	 * 	@param 	ignoreFieldNames	  忽略字段名称集合
	 * 	@return	FieldCacheObject
	 */
	public static FieldCacheObject create(Class<?> clazz, boolean ignoreStaticFields, List<String> ignoreFieldNames) {
		return new FieldCacheObject(clazz, ignoreStaticFields, ignoreFieldNames);
	}
}
